<?php
	/**
	 *作者：李克龙
	 *时间：2016-2-12
	 *描述：IP地址查询模块
	 *作用：用来查询IP地址并转换为具体物理位置
	 **/

namespace app\common\model;
	/**
	 * IP查询类
	 *
	 */
class Ip
{
    private $firstStartIpOffset;
    //索引区第一条流位置
    private $lastStartIpOffset;
    //索引区最后一条流位置
    private $prefixStartOffset;
    //前缀区第一条的流位置
    private $prefixEndOffset;
    //前缀区最后一条的流位置
    private $ipCount;
    //ip段数量
    private $prefixCount;
    //前缀数量
    private $fp;
    private $prefix_array = array();

    private $result =array();

    function __construct($database)
    {
        $this -> fp = @fopen($database, 'rb');
        $buf = $this -> read($this -> fp, 0, 16);
        $this -> firstStartIpOffset = $this -> BytesToLong($buf[0], $buf[1], $buf[2], $buf[3]);
        $this -> lastStartIpOffset = $this -> BytesToLong($buf[4], $buf[5], $buf[6], $buf[7]);
        $this -> prefixStartOffset = $this -> BytesToLong($buf[8], $buf[9], $buf[10], $buf[11]);
        $this -> prefixEndOffset = $this -> BytesToLong($buf[12], $buf[13], $buf[14], $buf[15]);
        $this -> ipCount = floor(($this -> lastStartIpOffset - $this -> firstStartIpOffset) / 12) + 1;
        $this -> prefixCount = floor(($this -> prefixEndOffset - $this -> prefixStartOffset) / 9) + 1;
        $pref_buf = $this -> read($this -> fp, $this -> prefixStartOffset, $this -> prefixCount * 9);
        for ($k = 0; $k < $this -> prefixCount; $k++)
        {
            $i = $k * 9;
            $start_index = $this -> BytesToLong($pref_buf[1 + $i], $pref_buf[2 + $i], $pref_buf[3 + $i], $pref_buf[4 + $i]);
            $end_index = $this -> BytesToLong($pref_buf[5 + $i], $pref_buf[6 + $i], $pref_buf[7 + $i], $pref_buf[8 + $i]);
            $this -> prefix_array[ord($pref_buf[$i])] = array(
                'start_index' => $start_index,
                'end_index' => $end_index
            );
        }
    }

    function __destruct()
    {
        if ($this -> fp !== NULL)
        {
            fclose($this -> fp);
        }
    }

    function formattojson($ip,$str)
    {

        $temp = explode('|', $str);
        $result = array(
            'continent'=>$temp[0],
            'country'=>$temp[1],
            'province'=>$temp[2],
            'city'=>$temp[3],
            'area'=>$temp[4],
            'line'=>$temp[5],
            'city_code'=>$temp[6],
            'en_country'=>$temp[7],
            'en_short_code'=>$temp[8],
            'longitude'=>$temp[9],
            'latitude'=>$temp[10],
            'intranet'=>FALSE,
        );
        if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))
        {
            $result["intranet"]=TRUE;
        }

        return $result;
    }

    function getlocation($ip_address)
    {
        if ($ip_address == '')
            return;
        $high = 0;
        $low = 0;
        $startIp = 0;
        $endIp = 0;
        $local_offset = 0;
        $local_length = 0;
        $temp = explode('.', $ip_address);

        $prefix = $temp[0];
        $ipNum = $this -> ip2uint($ip_address);
        if (array_key_exists($prefix, $this -> prefix_array))
        {
            $index = $this -> prefix_array[$prefix];
            $low = $index['start_index'];
            $high = $index['end_index'];
        }
        else
        {
            return "";
        }
        $left = $low == $high ? $low : $this -> BinarySearch($low, $high, $ipNum);
        $this -> GetIndex($left, $startIp, $endIp, $local_offset, $local_length);
        if (($startIp <= $ipNum) && ($endIp >= $ipNum))
        {
            return $this -> formattojson($ip_address,$this -> GetLocal($local_offset, $local_length));
        }
        else
        {
            return "";
        }
    }

    function BinarySearch($low, $high, $k)
    {
        $M = 0;
        while ($low <= $high)
        {
            $mid = floor(($low + $high) / 2);
            $endipNum = $this -> GetEndIp($mid);
            if ($endipNum >= $k)
            {
                $M = $mid;
                if ($mid == 0)
                {
                    break;
                }
                $high = $mid - 1;
            }
            else
                $low = $mid + 1;
        }
        return $M;
    }

    function GetIndex($left, &$startip, &$endip, &$local_offset, &$local_length)
    {
        $left_offset = $this -> firstStartIpOffset + ($left * 12);
        $buf = $this -> read($this -> fp, $left_offset, 12);
        $startip = $this -> BytesToLong($buf[0], $buf[1], $buf[2], $buf[3]);
        $endip = $this -> BytesToLong($buf[4], $buf[5], $buf[6], $buf[7]);
        $r3 = (ord($buf[8])<<0 | ord($buf[9])<<8 | ord($buf[10])<<16);
        if ($r3 < 0)
            $r3 += 4294967296;
        //负数时
        $local_offset = $r3;
        $local_length = ord($buf[11]);
    }

    function getEndIp($left)
    {
        $left_offset = $this -> firstStartIpOffset + ($left * 12) + 4;
        $buf = $this -> read($this -> fp, $left_offset, 4);
        return $this -> BytesToLong($buf[0], $buf[1], $buf[2], $buf[3]);
    }

    function GetLocal($local_offset, $local_length)
    {
        return $this -> read($this -> fp, $local_offset, $local_length);
    }

    function read($stream, $offset, $numberOfBytes)
    {
        if (fseek($stream, $offset) == 0)
        {
            $value = fread($stream, $numberOfBytes);
            return $value;
        }
    }

    function ip2uint($strIP)
    {
        $lngIP = ip2long($strIP);
        if ($lngIP < 0)
        {
            $lngIP += 4294967296;
            //负数时
        }
        return $lngIP;
    }

    function BytesToLong($a, $b, $c, $d)
    {
        $iplong = (ord($a)<<0) | (ord($b)<<8) | (ord($c)<<16) | (ord($d)<<24);
        if ($iplong < 0)
        {
            $iplong += 4294967296;
            //负数时
        };
        return $iplong;
    }

}
	
?>